#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
using std::ostream;

//数据的前面分配4字节用来存储引用计数
//指针 + 4直接偏移到数据的位置
class CowString{

public:
    CowString()
    :_pstr(new char[5]() + 4)
    {
        cout << "CowString()" << endl;
        init_refcount();
    }


    CowString(const char * pstr)
    :_pstr(new char[strlen(pstr) + 5]() + 4)
    {
        cout << "CowString(const char *)" << endl;
        strcpy(_pstr, pstr);
        init_refcount();
    }

    //浅拷贝
    CowString(const CowString & rhs)
    :_pstr(rhs._pstr)
    {
        cout << "CowString(const CowString &)" << endl;
        increase_refcount();
    }

    CowString & operator=(const CowString & rhs)
    {
        cout << "CowString operator=(const CowString &)" << endl;
        if(this != &rhs){
            //如果引用计数为1，则释放
            if(decrease_refcount() == 0){
                delete [] (_pstr - 4);
            }

            _pstr = rhs._pstr;
            increase_refcount();
        }
        return *this;
    }

    ~CowString(){
        cout << "~CowString()" << endl;
        if(decrease_refcount() == 0){
            delete [] (_pstr - 4);
        }
    }

    const char * c_str() const{
        return _pstr;
    }

    friend ostream & operator<<(ostream & os, const CowString & rhs);

//写时复制
//创建一个私有类，用来充当数组操作类似的作用实现
//为了节省空间，采用写时复制
//这样可以实现仅仅在使用数组方式修改内容
//的时候，才进行数据的拷贝，这样可以节省很多空间
//不会造成过多资源的浪费
//cow操作的核心思想就是写的时候在进行复制
//这样就可以减少资源的浪费，而且同时还减少了
//复制内存空间的时间消耗，只有特定情况下才会复制
//创建一个CowString类的内部类，
//让CowString的operator[]函数返回是
//这个新类型的对象，然后在这个新类型中
//对<<和=进行重载，让这两个运算符能够处理
//新类型对象，从而分开了处理逻辑。
/* ------------------------------- */

private:
    class CharProxy{
    public:
        CharProxy(CowString & CowString, size_t index)
        :_self(CowString)
        ,_index(index)
        {

        }

        //赋值，修改操作
        char & operator=(const char & c){
            if(_index <= _self.size()){
                if(_self.refcount() > 1){
                    char * ptmp = new char[_self.size() + 5]() + 4;
                    strcpy(ptmp, _self._pstr);
                    _self.decrease_refcount();
                    _self._pstr = ptmp;
                    _self.init_refcount();
                }
                _self._pstr[_index] = c;
                return _self._pstr[_index];
            }else {
                static char nullchar = '\0';
                return nullchar;
            }
        }
        
        friend ostream & operator<<(ostream & os,const CharProxy & rhs);
    
    private:
        CowString & _self;
        size_t _index;
    };
public:
    CharProxy operator[](size_t index){
        return CharProxy(*this, index);
    }

    friend ostream & operator<<(ostream & os, const CharProxy & rhs);

//重载[]运算符
#if 0
    char & operator[](size_t index){
        if(index <= size()){

            //修改,如果引用计数 > 1，需要复制空间
            if(*(int *)(_pstr - 4) > 1){
                char * ptmp = new char[size() + 5]() + 4;
                strcpy(ptmp, _pstr);
                //原来的引用计数 - 1
                --*(int *)(_pstr - 4);
                //现在的引用计数 = 1
                _pstr = ptmp;
                *(int *)(_pstr - 4) = 1;
            }
            return _pstr[index];
        }else {
            static char nullchar = '\0';
            return nullchar;
        }
    }
#endif
public:
    size_t refcount() const {
        return *(int *)(_pstr - 4);
    }

    void init_refcount(){
        *(int *)(_pstr - 4) = 1;
    }

    int increase_refcount(){
        return ++*(int *)(_pstr - 4);
    }

    int decrease_refcount(){
        return --*(int *)(_pstr - 4);
    }

    size_t size() const{
        return strlen(_pstr);
    }

private:
    char * _pstr;
};

ostream & operator<<(ostream & os, const CowString & rhs){
    if(rhs._pstr){
        os << rhs._pstr;
    }
    return os;
}
ostream & operator<<(ostream & os, const CowString::CharProxy & rhs){
    os << rhs._self._pstr[rhs._index];
    return os;
}

void test(){
    CowString s1("hello");
    cout << s1.refcount() << endl;
    CowString s2 = s1;
    cout << s1.refcount() << endl;
    cout << s2.refcount() << endl;

    cout << "=============" << endl;
    cout << s1[1] << endl;
    cout << s1.refcount() << endl;
    cout << s2.refcount() << endl;
    cout << "+++++++++++++++++++" << endl;
    s1[2] = 'p';
    cout << s1 << ":::" << s2 << endl;
    cout << s1.refcount() << endl;
    cout << s2.refcount() << endl;
}

int main()
{
    test();
    return 0;
}
